package com.atguigu.exam.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.exam.entity.*;
import com.atguigu.exam.exception.ExamException;
import com.atguigu.exam.mapper.*;
import com.atguigu.exam.service.AIService;
import com.atguigu.exam.service.QuestionService;
import com.atguigu.exam.utils.ExcelUtil;
import com.atguigu.exam.vo.AiGenerateRequestVo;
import com.atguigu.exam.vo.QuestionImportVo;
import com.atguigu.exam.vo.QuestionQueryVo;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 题目Service实现类
 * 实现题目相关的业务逻辑
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper,Question> implements QuestionService {

    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;

    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private PaperQuestionMapper paperQuestionMapper;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private AIService aiService;

    @Override
    public IPage<Question> getPageByCondition(Page<Question> questionPage, QuestionQueryVo queryVo) {
        return null;
    }

    @Override
    public void getPageByStream(Page<Question> questionPage, QuestionQueryVo queryVo) {

    }

    @Override
    public Question getQuestionDetailsById(Long id) {
        //根据id获取题目详情
        Question questionDetailsById = baseMapper.getQuestionDetailsById(id);
        //启动一个线程往Redis中存放热门题目
        new Thread(()->{
            //使用ZSet数据类型向Redis中存放热门题目
            redisTemplate.opsForZSet().incrementScore("question:popular",id,1);
        }).start();
        return questionDetailsById;
    }

    @Override
    public void addQuestion(Question question) {
        //判断在该类型下是否已经存在相同的题目
        boolean exists = baseMapper.exists(new LambdaQueryWrapper<Question>().eq(Question::getTitle, question.getTitle()).eq(Question::getType, question.getType()).eq(Question::getCategoryId, question.getCategoryId()));
        if(exists){
            //获取分类名
            Category category = categoryMapper.selectById(question.getCategoryId());
            //在某个分类下该类型的题目已经存在同名的题目
            throw new ExamException(777,"在<"+category.getName()+">分类下已经存在名字为 '"+question.getTitle()+"' 的题目");
        }
        //1.调用自带的方法插入题目信息
        baseMapper.insert(question);
        //获取数据库生成的题目id
        Long questionId = question.getId();
        //2.获取题目的答案
        QuestionAnswer answer = question.getAnswer();
        //设置题目的id
        answer.setQuestionId(questionId);

        //3.如果是选择题获取所有选项
        if("CHOICE".equals(question.getType())){
            //获取所有的选项
            List<QuestionChoice> choices = question.getChoices();
            if(!CollectionUtils.isEmpty(choices)){
                //创建一个StringBuilder对象
                StringBuilder stringBuilder = new StringBuilder();
                //遍历得到每一个选项
                for (int i = 0; i < choices.size(); i++) {
                    //获取每一个选项
                    QuestionChoice questionChoice = choices.get(i);
                    //设置题目id
                    questionChoice.setQuestionId(questionId);
                    //设置排序
                    questionChoice.setSort(i);
                    //判断当前选项是否是正确答案
                    if(questionChoice.getIsCorrect()){
                        //判断stringBuilder是否为空
                        if(stringBuilder.length() > 0){
                            //拼接一个逗号
                            stringBuilder.append(",");
                        }
                        //该选项是正确答案
                        stringBuilder.append((char)(i+65));
                    }

                    //向数据库中保存选择题的选项
                    questionChoiceMapper.insert(questionChoice);
                }
                //给选择题设置正确答案
                answer.setAnswer(stringBuilder.toString());
            }
        }

        //向数据库中插入题目的答案
        questionAnswerMapper.insert(answer);
    }

    @Override
    public void updateQuestion(Question question) {
        //判断当前题目是否已经存在
        boolean exists = baseMapper.exists(new LambdaQueryWrapper<Question>().eq(Question::getTitle, question.getTitle()).eq(Question::getType, question.getType()).eq(Question::getCategoryId, question.getCategoryId()).ne(Question::getId, question.getId()));
        if(exists){
            //获取分类名
            Category category = categoryMapper.selectById(question.getCategoryId());
            //在某个分类下该类型的题目已经存在同名的题目
            throw new ExamException(888,"在<"+category.getName()+">分类下已经存在名字为 '"+question.getTitle()+"' 的题目，请修改题目名称");
        }
        //将题目的更新日期设置为null
        question.setUpdateTime(null);
        //调用自带的方法根据id更新题目信息
        baseMapper.updateById(question);
        //获取题目的答案
        QuestionAnswer answer = question.getAnswer();
        //设置更新日期为null
        answer.setUpdateTime(null);
        //3.如果是选择题获取所有选项
        if("CHOICE".equals(question.getType())){
            //将原来所有的选项删除
            questionChoiceMapper.delete(new LambdaUpdateWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId,question.getId()));
            //获取所有的选项
            List<QuestionChoice> choices = question.getChoices();
            if(!CollectionUtils.isEmpty(choices)){
                //创建一个StringBuilder对象
                StringBuilder stringBuilder = new StringBuilder();
                //遍历得到每一个选项
                for (int i = 0; i < choices.size(); i++) {
                    //获取每一个选项
                    QuestionChoice questionChoice = choices.get(i);
                    //设置题目选项的id为null
                    questionChoice.setId(null);
                    //设置创建时间和更新时间为null
                    questionChoice.setCreateTime(null);
                    questionChoice.setUpdateTime(null);
                    //判断当前选项是否是正确答案
                    if(questionChoice.getIsCorrect()){
                        //判断stringBuilder是否为空
                        if(stringBuilder.length() > 0){
                            //拼接一个逗号
                            stringBuilder.append(",");
                        }
                        //该选项是正确答案
                        stringBuilder.append((char)(i+65));
                    }

                    //向数据库中保存选择题的选项
                    questionChoiceMapper.insert(questionChoice);
                }
                //给选择题设置正确答案
                answer.setAnswer(stringBuilder.toString());
            }
        }

        //更新题目的答案
        questionAnswerMapper.updateById(answer);


    }

    @Override
    public void deleteQuestionById(Long id) {
        //判断当前题目是否存在在某个试卷中
        Long count = paperQuestionMapper.selectCount(new LambdaQueryWrapper<PaperQuestion>().eq(PaperQuestion::getQuestionId, id));
        if(count > 0){
            //证明该题目存在于某个试卷中，不能删除
            throw new ExamException(999,"该题目存在于某个试卷中，不能删除");
        }
        //调用自带的方法删除题目
        baseMapper.deleteById(id);
        //删除题目的答案
        questionAnswerMapper.delete(new LambdaQueryWrapper<QuestionAnswer>().eq(QuestionAnswer::getQuestionId,id));
        //删除题目的选项
        questionChoiceMapper.delete(new LambdaQueryWrapper<QuestionChoice>().eq(QuestionChoice::getQuestionId,id));
    }

    @Override
    public List<Question> getPopularQuestions(Integer size) {
        //创建一个返回的集合
        List<Question> popularQuestions = new ArrayList<>();
        //获取Redis中的热门题目（根据评分从高到底排序）
        Set<Object> redisQuestionIds = redisTemplate.opsForZSet().reverseRange("question:popular", 0, -1);
        if(!CollectionUtils.isEmpty(redisQuestionIds)){
            //Redis中存在热门题目，遍历所有的热门题目的id
            redisQuestionIds.forEach(redisQuestionId -> {
                //根据题目id查询题目
                Question question = baseMapper.selectById(Long.valueOf(redisQuestionId.toString()));
                //将题目添加的返回的集合中
                popularQuestions.add(question);
            });
        }

        //判断Redis中的热门的题目的数量是否满足前端需要的热门题目的数量
        Integer diff = size - redisQuestionIds.size();
        if(diff > 0){
            //Redis中的热门的题目的数量不足，需要从数据库中查询最新的非Redis中的热门的题目
            //创建LabmdaQueryWrapper对象
            LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
            //封装查询条件
            //1.按照创建试卷倒叙排序
            questionLambdaQueryWrapper.orderByDesc(Question::getCreateTime);
            //2.id不在Redis中
            questionLambdaQueryWrapper.notIn(redisQuestionIds.size() > 0 ,Question::getId,redisQuestionIds);
            //3.在整个SQL后面添加一个 limit diff
            questionLambdaQueryWrapper.last("limit "+diff);
            //调用baseMapper中带条件查询的方法
            List<Question> databaseQuestions = baseMapper.selectList(questionLambdaQueryWrapper);
            //将从数据库中补充的热门题目添加到返回的集合中
            popularQuestions.addAll(databaseQuestions);
        }

        //给所有的热门题目添加答案和选项
        fillQuestionAnswerAndChoice(popularQuestions);
        return popularQuestions;
    }

    @Override
    public List<QuestionImportVo> previewExcel(MultipartFile file) throws IOException {
        //判断文件是否为空
        if(file == null || file.getSize() == 0){
            //文件为空，抛出异常
            throw new ExamException(999,"文件不能为空");
        }
        //判断是否是excel文件
        if(!file.getOriginalFilename().endsWith(".xls") && !file.getOriginalFilename().endsWith(".xlsx")){
            //抛出异常
            throw new ExamException(1000,"文件格式不正确，请上传excel文件");
        }
        //调用ExcelUil工具类中的方法解析Excel文件
        List<QuestionImportVo> questionImportVos = ExcelUtil.parseExcel(file);
        return questionImportVos;
    }

    @Override
    public Integer importQuestions(List<QuestionImportVo> questions) {
        //判断是否有数据
        if(CollectionUtils.isEmpty(questions)){
            //没有数据，抛出异常
            throw new ExamException(1001,"没有导入任何题目，请检查数据");
        }
        //设置一个变量统计导入成功的数量
        Integer successCount = 0;
        //遍历所有的题目
        for (int i = 0; i < questions.size(); i++) {
            //获取每一个题目
            QuestionImportVo questionImportVo = questions.get(i);
            //将QuestionImportVo转换为Question类型
            Question question = convertQuestionImportVoToQuestion(questionImportVo);
            //调用自定义的方法向数据库中插入题目、题目答案、题目选项
            addQuestion(question);
            //将成功的数量加1
            successCount++;
        }
        return successCount;
    }

    @Override
    public List<QuestionImportVo> generateQuestionsByAi(AiGenerateRequestVo request) {
        //1.生成提示词
        String prompt = buildPrompt(request);
        //2.调用AI大模型
        String content = aiService.callAi(prompt);
        //3.解析结果
        int startIndex = content.indexOf("```json");
        int endIndex = content.lastIndexOf("```");
        //保证有数据，且下标正确！
        if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
            //获取真正结果
            String realResult = content.substring(startIndex+7,endIndex);
            System.out.println("realResult = " + realResult);
            JSONObject jsonObject = JSONObject.parseObject(realResult);
            JSONArray questions = jsonObject.getJSONArray("questions");
            List<QuestionImportVo> questionImportVoList = new ArrayList<>();
            for (int i = 0; i < questions.size(); i++) {
                //获取对象
                JSONObject questionJson = questions.getJSONObject(i);
                QuestionImportVo questionImportVo = new QuestionImportVo();
                questionImportVo.setTitle(questionJson.getString("title"));
                questionImportVo.setType(questionJson.getString("type"));
                questionImportVo.setMulti(questionJson.getBoolean("multi"));
                questionImportVo.setDifficulty(questionJson.getString("difficulty"));
                questionImportVo.setScore(questionJson.getInteger("score"));
                questionImportVo.setAnalysis(questionJson.getString("analysis"));
                questionImportVo.setCategoryId(request.getCategoryId());

                //选择题处理选项
                if ("CHOICE".equals(questionImportVo.getType())) {
                    JSONArray choices = questionJson.getJSONArray("choices");
                    List<QuestionImportVo.ChoiceImportDto> choiceImportDtoList = new ArrayList<>(choices.size());
                    for (int i1 = 0; i1 < choices.size(); i1++) {
                        JSONObject choicesJSONObject = choices.getJSONObject(i1);
                        QuestionImportVo.ChoiceImportDto choiceImportDto = new QuestionImportVo.ChoiceImportDto();
                        choiceImportDto.setContent(choicesJSONObject.getString("content"));
                        choiceImportDto.setIsCorrect(choicesJSONObject.getBoolean("isCorrect"));
                        choiceImportDto.setSort(choicesJSONObject.getInteger("sort"));
                        choiceImportDtoList.add(choiceImportDto);
                    }
                    questionImportVo.setChoices(choiceImportDtoList);
                }
                //答案 [判断题！ TRUE |FALSE  false true  f  t 是 否]
                questionImportVo.setAnswer(questionJson.getString("answer"));
                questionImportVoList.add(questionImportVo);
            }
            return questionImportVoList;
        }
        throw new RuntimeException("ai生成题目json数据结构错误，无法正常解析！数据为：%s".formatted(content));
    }

    //将QuestionImportVo转换为Question类型
    private Question convertQuestionImportVoToQuestion(QuestionImportVo questionImportVo) {
        //创建一个Question对象
        Question question = new Question();
        //调用BeanUtils工具类中的方法复制相同属性的属性值
        BeanUtils.copyProperties(questionImportVo,question);
        //创建QuestionAnswer对象
        QuestionAnswer questionAnswer = new QuestionAnswer();
        //对于判断题和简答题设置答案
        if("JUDGE".equals(questionImportVo.getType())){
            //如果是判断题，将问题的答案转换为大写
            questionAnswer.setAnswer(questionImportVo.getAnswer().toUpperCase());
        }else{
            questionAnswer.setAnswer(questionImportVo.getAnswer());
        }

        //设置keywards
        questionAnswer.setKeywords(questionImportVo.getKeywords());

        //给题目设置题目答案
        question.setAnswer(questionAnswer);

        //判断是否是选择题
        if("CHOICE".equals(questionImportVo.getType())){
            //获取所有的选项
            List<QuestionImportVo.ChoiceImportDto> choices = questionImportVo.getChoices();
            //创建一个List<QuestionChoice>集合
            List<QuestionChoice> questionChoices = new ArrayList<>();
            if(!CollectionUtils.isEmpty(choices)){
                //遍历choices中的所有选项
                choices.forEach(choice -> {
                    //创建QuestionChoice对象
                    QuestionChoice questionChoice = new QuestionChoice();
                    //复制属性值
                    BeanUtils.copyProperties(choice,questionChoice);
                    //将QuestionChoice对象添加到List<QuestionChoice>集合中
                    questionChoices.add(questionChoice);
                });
            }
            //将选择题的选项设置道题目中
            question.setChoices(questionChoices);

        }
        return question;
    }

    //给题目设置题目答案和题目选项的方法
    private void fillQuestionAnswerAndChoice(List<Question> records) {
        if(!CollectionUtils.isEmpty(records)){
            //获取所有题目的id
            List<Long> qustionIds = records.stream().map(Question::getId).collect(Collectors.toList());
            //调用QuestionAnswerMapper中的方法查询所有题目的答案
            List<QuestionAnswer> questionAnswers = questionAnswerMapper.selectList(new LambdaQueryWrapper<QuestionAnswer>().in(QuestionAnswer::getQuestionId, qustionIds));
            //调用QuestionChoiceMapper中的方法查询所有选择题的选项
            List<QuestionChoice> questionChoices = questionChoiceMapper.selectList(new LambdaQueryWrapper<QuestionChoice>().in(QuestionChoice::getQuestionId, qustionIds));
            //将List<QuestionAnswer>转换为Map<Long,QuestionAnswer>
            Map<Long, QuestionAnswer> questionAnswerMap = questionAnswers.stream().collect(Collectors.toMap(
                    QuestionAnswer::getQuestionId,
                    qa -> qa
            ));
            //将List<QuestionChoice>转换为Map<Long,List<QuestionChoice>>
            Map<Long, List<QuestionChoice>> questionChoicesMap = questionChoices.stream().collect(Collectors.groupingBy(QuestionChoice::getQuestionId));

            //遍历records中的所有题目
            records.forEach(question -> {
                //获取当前题目的id
                Long questionId = question.getId();
                //从questionAnswerMap中获取题目的答案
                QuestionAnswer questionAnswer = questionAnswerMap.get(questionId);
                //将问题的答案设置到题目中
                question.setAnswer(questionAnswer);
                //判断是否是选择题
                if("CHOICE".equals(question.getType())){
                    //从questionChoicesMap中获取选择题的所有选项
                    List<QuestionChoice> questionChoices1 = questionChoicesMap.get(questionId);
                    if(!CollectionUtils.isEmpty(questionChoices1)){
                        //按照选项中的sort进行升序排序
                        questionChoices1.sort(Comparator.comparing(QuestionChoice::getSort));
                        //将选项设置到题目中
                        question.setChoices(questionChoices1);
                    }
                }
            });
        }
    }

    /**
     * 构建发送给AI让AI出题的提示词
     */
    public String buildPrompt(AiGenerateRequestVo request) {
        StringBuilder prompt = new StringBuilder();

        prompt.append("请为我生成").append(request.getCount()).append("道关于【")
                .append(request.getTopic()).append("】的题目。\n\n");

        prompt.append("要求：\n");

        // 题目类型要求
        if (request.getTypes() != null && !request.getTypes().isEmpty()) {
            List<String> typeList = Arrays.asList(request.getTypes().split(","));
            prompt.append("- 题目类型：");
            for (String type : typeList) {
                switch (type.trim()) {
                    case "CHOICE":
                        prompt.append("选择题");
                        if (request.getIncludeMultiple() != null && request.getIncludeMultiple()) {
                            prompt.append("(包含单选和多选)");
                        }
                        prompt.append(" ");
                        break;
                    case "JUDGE":
                        prompt.append("判断题（**重要：确保正确答案和错误答案的数量大致平衡，不要全部都是正确或错误**） ");
                        break;
                    case "TEXT":
                        prompt.append("简答题 ");
                        break;
                }
            }
            prompt.append("\n");
        }

        // 难度要求
        if (request.getDifficulty() != null) {
            String difficultyText = switch (request.getDifficulty()) {
                case "EASY" -> "简单";
                case "MEDIUM" -> "中等";
                case "HARD" -> "困难";
                default -> "中等";
            };
            prompt.append("- 难度等级：").append(difficultyText).append("\n");
        }

        // 额外要求
        if (request.getRequirements() != null && !request.getRequirements().isEmpty()) {
            prompt.append("- 特殊要求：").append(request.getRequirements()).append("\n");
        }

        // 判断题特别要求
        if (request.getTypes() != null && request.getTypes().contains("JUDGE")) {
            prompt.append("- **判断题特别要求**：\n");
            prompt.append("  * 确保生成的判断题中，正确答案(TRUE)和错误答案(FALSE)的数量尽量平衡\n");
            prompt.append("  * 不要所有判断题都是正确的或都是错误的\n");
            prompt.append("  * 错误的陈述应该是常见的误解或容易混淆的概念\n");
            prompt.append("  * 正确的陈述应该是重要的基础知识点\n");
        }

        prompt.append("\n请严格按照以下JSON格式返回，不要包含任何其他文字：\n");
        prompt.append("```json\n");
        prompt.append("{\n");
        prompt.append("  \"questions\": [\n");
        prompt.append("    {\n");
        prompt.append("      \"title\": \"题目内容\",\n");
        prompt.append("      \"type\": \"CHOICE|JUDGE|TEXT\",\n");
        prompt.append("      \"multi\": true/false,\n");
        prompt.append("      \"difficulty\": \"EASY|MEDIUM|HARD\",\n");
        prompt.append("      \"score\": 5,\n");
        prompt.append("      \"choices\": [\n");
        prompt.append("        {\"content\": \"选项内容\", \"isCorrect\": true/false, \"sort\": 1}\n");
        prompt.append("      ],\n");
        prompt.append("      \"answer\": \"TRUE或FALSE(判断题专用)|文本答案(简答题专用)\",\n");
        prompt.append("      \"analysis\": \"题目解析\"\n");
        prompt.append("    }\n");
        prompt.append("  ]\n");
        prompt.append("}\n");
        prompt.append("```\n\n");

        prompt.append("注意：\n");
        prompt.append("1. 选择题必须有choices数组，判断题和简答题设置answer字段\n");
        prompt.append("2. 多选题的multi字段设为true，单选题设为false\n");
        prompt.append("3. **判断题的answer字段只能是\"TRUE\"或\"FALSE\"，请确保答案分布合理**\n");
        prompt.append("4. 每道题都要有详细的解析\n");
        prompt.append("5. 题目要有实际价值，贴近实际应用场景\n");
        prompt.append("6. 严格按照JSON格式返回，确保可以正确解析\n");

        // 如果只生成判断题，额外强调答案平衡
        if (request.getTypes() != null && request.getTypes().equals("JUDGE") && request.getCount() > 1) {
            prompt.append("7. **判断题答案分布要求**：在").append(request.getCount()).append("道判断题中，");
            int halfCount = request.getCount() / 2;
            if (request.getCount() % 2 == 0) {
                prompt.append("请生成").append(halfCount).append("道正确(TRUE)和").append(halfCount).append("道错误(FALSE)的题目");
            } else {
                prompt.append("请生成约").append(halfCount).append("-").append(halfCount + 1).append("道正确(TRUE)和约").append(halfCount).append("-").append(halfCount + 1).append("道错误(FALSE)的题目");
            }
        }

        return prompt.toString();
    }

}